package com.zzyq.pc.web.report.serviceImpl;

import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
import com.zzyq.pc.utils.DateUtils;
import com.zzyq.pc.utils.JsonUtils;
import com.zzyq.pc.utils.ReportUtils;
import com.zzyq.pc.web.report.base.TableField;
import com.zzyq.pc.web.report.response.ModuleData;
import com.zzyq.pc.web.report.service.IDocExportor;

public class PdfDocExportor extends PdfPageEventHelper implements IDocExportor {

	private Document doc = new Document(PageSize.A4.rotate());

	public Document getDoc() {
		return doc;
	}

	private Map<DocFont, Font> fonts = null;

	private Map<DocFont, Font> getFont(DocFont... font) throws DocumentException, IOException {
		Map<DocFont, Font> fonts = new HashMap<>();
		BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
		for (DocFont docFont : font) {
			fonts.put(docFont, new Font(bfChinese, docFont.getSize(), docFont.getBold() ? Font.BOLD : Font.NORMAL));
		}
		return fonts;
	}

	public PdfDocExportor() {
		try {
			fonts = getFont(DocFont.values());
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private int getAlignment(Align align) {
		int alignment = 1;
		switch (align) {
		case Center:
			alignment = Element.ALIGN_CENTER;
			break;
		case Left:
			alignment = Element.ALIGN_LEFT;
			break;
		case Right:
			alignment = Element.ALIGN_RIGHT;
			break;
		default:
			alignment = Element.ALIGN_CENTER;
			break;
		}
		return alignment;
	}

	public int getPercent2(float h, float w) {
		int p = 0;
		float p2 = 0.0f;
		p2 = 530 / w * 100;
		p = Math.round(p2);
		return p;
	}

	@Override
	public IDocExportor createDoc(Map<String, String> infos, List<ModuleData> moduleDatas, Map<String, String> svg,
			OutputStream out) {
		try {
			PdfWriter writer = PdfWriter.getInstance(getDoc(), out);
			writer.setStrictImageSequence(true);
			doc.open();
			writeLine();
			// 写入报告的标题信息
			writeText(infos.get(GAROUP_NAME), DocFont.TitleDoc, Align.Center);
			// writeText(infos.get(RPT_NAME), DocFont.TitleDoc, Align.Center);
			writeLine();
			// 遍历获取到的模块数据信息，依次写入Word
			for (ModuleData moduleData : moduleDatas) {
				// 判断模块是否显示
//				if (!moduleData.getShow()) {
//					continue;
//				}
				// 写入模块标题
				String moduleTitle = moduleData.getTitle();
				if (StringUtils.isNotBlank(moduleTitle)) {
				writeLine();
				writeText(moduleTitle, DocFont.TitleModule, Align.Left);
				writeLine();
				}
				// 写入模块的文字信息
				String moduleText = moduleData.getText();
				if (StringUtils.isNotBlank(moduleText)) {
					writeText(moduleText, DocFont.Content, Align.Left);
					writeLine();
				}
				// 开始插入图片
				String svgStr = svg.get(moduleData.getId() + "");
				// 根据展示类型判断是否加载并写入图片>2表示表格以上
				if (moduleData.getTypeId() > 2 && StringUtils.isNotBlank(svgStr)) {
					// byte[] bytes = ReportUtils.convertToPngByte(svgStr);
					byte[] bytes = Base64.decodeBase64(svgStr);
					try {
						writeImage(bytes, null, null);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} else if (moduleData.getTypeId() == 2 && !moduleData.getDataList().isEmpty()
						&& !moduleData.getTableFields().isEmpty()) {// 绘制表格
					// 将数据信息转换为json数据
					String dataJson = JsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
					// 计算行数
					int rowSize = 0;
					if (moduleData.isPullByList()) {
						if (null != moduleData.getDataList().get(0).datas) {
						rowSize = moduleData.getDataList().size() * moduleData.getDataList().get(0).getDatas().size();
						} else {
							rowSize = moduleData.getDataList().size()
									* moduleData.getDataList().get(0).getDatas2().size();
						}
					} else {
						if (null != moduleData.getDataList().get(0).datas) {
							rowSize = moduleData.getDataList().get(0).getDatas().size();
						}else{
							rowSize = moduleData.getDataList().get(0).getDatas2().size();
						}
					}
					writeTable(moduleData.getTableFields(), dataJson, moduleData.isPullByList(), rowSize);
				} else if (moduleData.getTypeId() == 1 && moduleData.getDataList() != null
						&& moduleData.getDataList().size() > 0) {// 填充文字列表
					String dataJson = JsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
					writeTextList(moduleData.getTableFields(), dataJson);
				}
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	@Override
	public void writeToStream() throws Exception {
		doc.close();
	}

	@Override
	public void writeTable(List<TableField> fields, String dataJson, boolean isPullByList, int rowSize)
			throws Exception {
		// 获取列数量
		int colSize = fields.size();
		// 根据计算的行列来新建一个保存表格元素的二维数组（这里的行数不包含标题项）
		PdfPCell[][] cells = new PdfPCell[rowSize][colSize];
		// 从json数据中解析出树
		JsonNode root = new ObjectMapper().readTree(dataJson);
		// 拿到文档的总宽度
		Rectangle rectangle = getDoc().getPageSize();
		float width = rectangle.getWidth() - 80;
		// 这里计算每一列的宽度占比
		float[] widths = new float[colSize];
		for (int i = 0; i < colSize; i++) {
			widths[i] = width * fields.get(i).getWidthPercent();
		}
		// 开始生成表格
		PdfPTable table = new PdfPTable(colSize);
		// 设置宽度
		table.setTotalWidth(widths);
		table.setLockedWidth(true);
		BaseColor lightGrey = new BaseColor(0xCC, 0xCC, 0xCC);
		BaseColor firstColor = new BaseColor(77, 83, 97);
		// 下面的for生成标题栏
		for (TableField field : fields) {
			// 设置标题信息
			Font font = new Font(fonts.get(DocFont.TitleModule));
			font.setColor(BaseColor.WHITE);
			Paragraph titlePhrase = new Paragraph(field.getTitle(), font);
			PdfPCell titleCell = new PdfPCell(titlePhrase);
			// 设置标题高度和样式
			titleCell.setFixedHeight(TABLE_HEIGHT);
			titleCell.setHorizontalAlignment(Element.ALIGN_CENTER);
			titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
			titleCell.setBorderColor(lightGrey);
			titleCell.setBackgroundColor(firstColor);
			// 添加标题到表格中
			table.addCell(titleCell);
		}
		// 行数计数器.为了精准添加数据到表格的坐标而计算的行数值
		int row = 0;
		// 开始解析并填充表格数据
		Iterator<JsonNode> moduleDataIterator = root.elements();
		while (moduleDataIterator.hasNext()) {
			// 如果不是按照普通的表格排列，则表格的行数不是数据类型列表数*值列表数，而只是数据列表数
			// 故重新设置行数为0
			if (!isPullByList) {
				row = 0;
			}
			// 拿出第一层数据列表
			JsonNode typeData = moduleDataIterator.next();
			// 获取这个数据的标题
			String dataName = typeData.findValue("title").asText();
			// 获取到这一行的数据
			JsonNode dataList = typeData.path("datas");
			// 获取到这一行的数据
			JsonNode dataList2 = typeData.path("datas2");
			// 在类型数据中获取真正值数据的迭代。
			Iterator<JsonNode> dataIterator = dataList.elements();
			if (!dataIterator.hasNext()) {
				dataIterator = dataList2.elements();
			}
			while (dataIterator.hasNext()) {
				// 这个data代表一个数据信息，即最终取到的包含值的对象
				JsonNode data = dataIterator.next();
				// 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值
				for (int i = 0; i < colSize; i++) {
					// 获取这一列定义的的标题信息
					TableField field = fields.get(i);
					PdfPCell cell = null;
					// 拼接显示的文本，定义一个文本对象
					StringBuilder value = new StringBuilder();
					String dataListField = field.getDataListField();
					String valueField = field.getValueField();
					// 格式化文本的format
					String textFormat = field.getTextFormat();
					// 格式化日期的format
					String dateFormat = field.getTimeFormat();
					// 超链接的field
					String urlField = field.getUrlField();

					// 若匹配到此列跟此行数据来源一致
					if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
							&& dataListField.equals(dataName)) {
						String str = "";
						// 首先判断是否需要自增的序号
						if (field.isIncr()) {
							value.append((row + 1) + "");
						}
						// 取出文本值
						if (StringUtils.isNotBlank(valueField)) {
							try {
								str = data.findValue(valueField).asText();
							} catch (Exception e) {
								e.printStackTrace();
							}
							if (StringUtils.isNotBlank(textFormat)) {
								// 判断是否需要格式化填充文本
								// str = String.format(textFormat, str);
								str = str + textFormat;
							} else if (StringUtils.isNotBlank(dateFormat)) {
								// 判断是否需要格式化文本日期
								try {
									str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
											field.getTimeFormat());
								} catch (Exception e) {
								}
							}
							// 最后拼接文本则为最后显示的
							value.append(str);
						}
						// 这里字体需要新建对象，否则影响其他的字体
						Font font = fonts.get(DocFont.Content);
						if (i == 0) {
							// 为第一列时，由于背景为黑色，字体需要为白色
							font = new Font(fonts.get(DocFont.Content));
							font.setColor(BaseColor.WHITE);
						}
						// 创建字体和表格元素对象
						Chunk chunk = new Chunk(value.toString(), font);
						// 添加超链接
						if (StringUtils.isNotBlank(urlField)) {
							chunk.setAnchor(data.findValue(urlField).asText());
						}
						Paragraph cellPhrase = new Paragraph();
						cellPhrase.add(chunk);
						cell = new PdfPCell(cellPhrase);
						cell.setFixedHeight(TABLE_HEIGHT);
						// 设置此条数据是否需要居中显示
						cell.setHorizontalAlignment(field.isCenter() ? Element.ALIGN_CENTER : Element.ALIGN_LEFT);
						cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
						cell.setBorderColor(lightGrey);
						// cell.setPaddingTop(-2f);//把字垂直居中
						// cell.setPaddingBottom(8f);//把字垂直居中
						if (i == 0) {
							cell.setBackgroundColor(firstColor);
						}
						// 将表格元素加入到对应位置的二维数组中
						cells[row][i] = cell;
					} else {
						// 没有找到匹配的field字段
						continue;
					}

				}
				// 每循环一个数据对象，行数+1
				row++;
			}
		}
		// 添加数据进表格中表格
		for (PdfPCell[] cellsCols : cells) {
			for (PdfPCell cellsRow : cellsCols) {
				table.addCell(cellsRow == null ? new PdfPCell() : cellsRow);
			}
		}
		// 表格添加到文档
		getDoc().add(table);

	}

	@Override
	public void writeTextList(List<TableField> fields, String dataJson) throws Exception {
		// 获取列数量
		int colSize = fields.size();
		// 从json数据中解析出树
		JsonNode root = new ObjectMapper().readTree(dataJson);
		int row = 0;
		// 开始解析并填充表格数据
		Iterator<JsonNode> moduleDataIterator = root.elements();
		while (moduleDataIterator.hasNext()) {
			// 拿出第一层数据列表
			JsonNode typeData = moduleDataIterator.next();
			// 获取这个数据的标题
			String dataName = typeData.findValue("title").asText();
			// 获取到这一行的数据
			JsonNode dataList = typeData.path("datas");
			// 在类型数据中获取真正值数据的迭代。
			Iterator<JsonNode> dataIterator = dataList.elements();
			while (dataIterator.hasNext()) {
				// 这个data代表一个数据信息，即最终取到的包含值的对象
				JsonNode data = dataIterator.next();
				// 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值
				for (int i = 0; i < colSize; i++) {
					// 获取这一列定义的的标题信息
					TableField field = fields.get(i);
					// 拼接显示的文本，定义一个文本对象
					StringBuilder value = new StringBuilder();
					String dataListField = field.getDataListField();
					String valueField = field.getValueField();
					// 格式化文本的format
					String textFormat = field.getTextFormat();
					// 格式化日期的format
					String dateFormat = field.getTimeFormat();

					// 若匹配到此列跟此行数据来源一致
					if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
							&& dataListField.equals(dataName)) {
						String str = "";
						// 首先判断是否需要自增的序号
						if (field.isIncr()) {
							value.append((row + 1) + "");
						}
						// 取出文本值
						if (StringUtils.isNotBlank(valueField)) {
							str = data.findValue(field.getValueField()).asText();
							if (StringUtils.isNotBlank(textFormat)) {
								// 判断是否需要格式化填充文本
								str = String.format(textFormat, str);
							} else if (StringUtils.isNotBlank(dateFormat)) {
								// 判断是否需要格式化文本日期
								try {
									str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
											field.getTimeFormat());
								} catch (Exception e) {
								}
							}
							// 最后拼接文本则为最后显示的
							value.append(str);
						}
						// 创建字体和表格元素对象
						// 这里字体需要新建对象，否则影响其他的字体
						Font font = fonts.get(DocFont.Content);
						if (i == 0) {
							// 为第一列时，由于背景为黑色，字体需要为白色
							font = new Font(fonts.get(DocFont.Content));
							font.setStyle(Font.BOLD);
						}
						Paragraph paragraph = new Paragraph(value.toString(), font);
						paragraph.setFirstLineIndent(20);
						getDoc().add(paragraph);

					} else {
						// 没有找到匹配的field字段
						continue;
					}
				}
				// 每循环一个数据对象，行数+1
				writeLine();
				row++;
			}
		}

	}

	@Override
	public void writeImage(byte[] bytes, Dimension size, Align align) throws Exception {
		if (size == null) {
			size = ReportUtils.getImgDimension(new ByteArrayInputStream(bytes));
		}
		if (size == null) {
			return;
		}
		if (align == null) {
			align = Align.Center;
		}
		Image image = Image.getInstance(bytes);
		image.setAlignment(getAlignment(align));
		image.scalePercent(getPercent2(image.getHeight(), image.getWidth()) + 3);
		getDoc().add(image);

	}

	@Override
	public void writeText(String text, DocFont font, Align align) throws Exception {
		Paragraph paragraph = new Paragraph(text, fonts.get(font));
		paragraph.setAlignment(getAlignment(align));
		getDoc().add(paragraph);

	}

	@Override
	public void writeLine() throws Exception {
		Paragraph br = new Paragraph("\n", fonts.get(DocFont.Content));
		getDoc().add(br);
	}

}
